from datetime import datetime, date, timedelta

import xlsxwriter
from sqlalchemy import and_, or_
from flask import send_file, request, jsonify, g

from aab import app
from aab.model import Leave, User, db, Group, Roles
from aab.utils import fill_worksheet, success, approval_to_dict, login_required, jsonify_error, do_approval, \
    DATE_FORMAT, SheetData

translation = {
    'office': '办公区',
    'south': '大院'
}


@app.route('/xlsx/leave/<addr_type>', methods=['GET'])
@app.route('/xlsx/leave/<addr_type>/<day>', methods=['GET'])
@login_required(roles=[Roles.ADMIN])
def generate_leave_xlsx(addr_type, day=None):
    if day is None:
        day = date.today()
        is_order_time = False
    else:
        day = datetime.strptime(day, DATE_FORMAT).date()
        is_order_time = True
    return _generate_leave_xlsx(translation.get(addr_type, addr_type), day, is_order_time)


@app.route('/xlsx/leave-single-week/<addr_type>', methods=['GET'])
def generate_single_week_leave_xlsx(addr_type):
    addr_type = translation.get(addr_type, addr_type)
    today = date.today()
    begin = today + timedelta(days=-today.weekday())
    end = today + timedelta(days=7 - today.weekday())
    begin_time = datetime.combine(begin, datetime.min.time())
    end_time = datetime.combine(end, datetime.min.time())

    def f(query):
        return query.filter_by(is_single=True).filter(or_(
            and_(Leave.leave_time >= begin_time, Leave.leave_time < end_time),
            and_(Leave.return_time >= begin_time, Leave.return_time < end_time),
        ))

    return _generate_leave_xlsx(addr_type, filter_func=f, file_name_prefix='leave-single-week')


def _generate_leave_xlsx(addr_type, day=None, is_order_day=False, filter_func=None, file_name_prefix='leave'):
    if day:
        path = '/tmp/{}-{}-{}__{}.xlsx'.format(file_name_prefix, addr_type,
                                               day.strftime('%Y-%m-%d'),
                                               datetime.now().strftime('%Y-%m-%d-%H-%M-%S'))
    else:
        path = '/tmp/{}-{}__{}.xlsx'.format(file_name_prefix, addr_type, datetime.now().strftime('%Y-%m-%d-%H-%M-%S'))

    worksheet = SheetData(path)

    if addr_type == '办公区':
        fields = [
            ('进入时间', 'return_time'),
            ('离开时间', 'leave_time'),
            ('事由', 'reason'),
            ('填表时间', 'order_time'),
        ]
    else:
        fields = [
            ('进/出', lambda d: '进' if d.is_in else '出'),
            ('是否单身干部', lambda d: '是' if d.is_single else '否'),
            ('外出时间', 'leave_time'),
            ('返回时间', 'return_time'),
            ('交通工具', 'traffic'),
            ('外出/进入地点', 'location'),
            ('密切接触人员', 'persons'),
            ('事由', 'reason'),
            ('填表时间', 'order_time')
        ]

    worksheet.write(0, 0, '人员编号'),
    for j, field in enumerate(fields):
        worksheet.write(0, j + 1, field[0])

    query = Leave.query.filter_by(addr_type=addr_type)
    if day:
        if is_order_day:
            yesterday = day + timedelta(days=-1)
            begin_order_time = datetime.combine(yesterday, datetime.min.time()) + timedelta(hours=12)
            end_order_time = datetime.combine(day, datetime.min.time()) + timedelta(hours=12)
            query = query.filter(and_(Leave.order_time >= begin_order_time, Leave.order_time <= end_order_time))
        else:
            query = query.filter(db.func.date(Leave.leave_time) >= day)
    elif filter_func:
        query = filter_func(query)

    for i, leave in enumerate(query):
        worksheet.write(i + 1, 0, leave.user.number)
        fill_worksheet(worksheet, leave, i, fields)

    return worksheet.send_file()


@app.route('/user/<user_number>/leaves', methods=['POST'])
@login_required
def add_leave(user_number):
    if g.auth.user_number != user_number:
        return jsonify_error(403, message="只能添加自己的进出申请")

    user = User.query.filter_by(number=user_number).scalar()
    if user is None:
        return jsonify_error(400, message="用户不存在")

    data = request.get_json()
    if data is None:
        return jsonify_error(400, message='Require JSON data')

    leave = Leave(user=user).update_from_dict(data)
    leave.order_time = datetime.now()
    db.session.add(leave)
    db.session.commit()
    return success()


@app.route('/leave/<leave_id>', methods=['DELETE'])
@login_required
def delete_leave(leave_id):
    leave = Leave.query.filter_by(id=leave_id).scalar()
    if leave is not None:
        db.session.delete(leave)
        db.session.commit()
    return success()


@app.route('/user/<user_number>/leaves/<addr_type>', methods=['GET'])
@app.route('/user/<user_number>/leaves', methods=['GET'])
@login_required
def query_leaves(user_number, addr_type=None):
    user = User.query.filter_by(number=user_number).scalar()
    if user is None:
        return jsonify_error(404, message="User {} NOT FOUND".format(user_number))

    day = date.today()
    if addr_type:
        leaves = Leave.query.filter_by(user=user, addr_type=addr_type).filter(
            db.func.date(Leave.leave_time) >= day).all()
    else:
        leaves = Leave.query.filter_by(user=user).filter(db.func.date(Leave.leave_time) >= day).all()
    leaves = [_leave_to_dict(leave) for leave in leaves]
    return jsonify(leaves)


def _leave_to_dict(leave):
    d = leave.to_dict()
    d['approval'] = approval_to_dict(leave.approval)
    return d


@app.route('/leaves/<addr_type>', methods=['GET'])
@app.route('/leaves', methods=['GET'])
@login_required(roles=[Roles.ADMIN])
def query_all_leaves(addr_type=None):
    day = date.today()
    now = datetime.now()
    if addr_type:
        leaves = Leave.query.filter_by(addr_type=addr_type) \
            .filter(db.func.date(Leave.leave_time) >= day) \
            .order_by(Leave.order_time.desc()) \
            .all()
    else:
        leaves = Leave.query.filter(and_(Leave.leave_time >= now,
                                         Leave.return_time >= now)) \
            .order_by(Leave.order_time.desc()) \
            .all()
    return jsonify(_leaves_to_dict(leaves))


@app.route('/group/<group_name>/leaves/<addr_type>', methods=['GET'])
@app.route('/group/<group_name>/leaves', methods=['GET'])
@login_required(roles=[Roles.ADMIN, Roles.GROUP_MANAGER])
def query_group_leaves(group_name, addr_type=None):
    group = Group.query.filter_by(name=group_name).scalar()
    if group is None:
        return jsonify_error(404, '组不存在')

    day = date.today()
    now = datetime.now()
    if addr_type:
        leaves = Leave.query.join(User).filter(User.group == group).filter(Leave.addr_type == addr_type) \
            .filter(db.func.date(Leave.leave_time) >= day) \
            .order_by(Leave.order_time.desc()) \
            .all()
    else:
        leaves = Leave.query.join(User).filter(User.group == group) \
            .filter(and_(Leave.leave_time >= now,
                         Leave.return_time >= now)) \
            .order_by(Leave.order_time.desc()) \
            .all()

    return jsonify(_leaves_to_dict(leaves))


def _leaves_to_dict(leaves):
    result = []
    for leave in leaves:
        d = _leave_to_dict(leave)
        d['user_number'] = leave.user.number
        result.append(d)
    return result


@app.route('/leave/<leave_id>/approval', methods=['PUT'])
@login_required(roles=[Roles.ADMIN, Roles.GROUP_MANAGER])
def approve_leave(leave_id):
    data = request.get_json()
    if data is None:
        return jsonify_error(400, message='Require JSON data')

    leave = Leave.query.filter_by(id=leave_id).scalar()
    if leave is None:
        return jsonify_error(404, message='进出申请未找到')

    return do_approval(leave, data.get('is_reject', False), data.get('to_admin', True))
